最佳实践
- Production System Tips
- JobDataMap Tips
- Trigger Tips
- JDBC JobStore
- Daylight Savings Time
- Jobs
- Listeners (TriggerListener, JobListener, SchedulerListener
- Exposing Scheduler Functionality Through Applications
生产环境中的建议
跳过检查更新
通过配置 “org.quartz.scheduler.skipUpdateCheck: true” 属性,你能够取消检查更新。
JobDataMap 建议
Only Store Primitive Data Types (including Strings) In the JobDataMap,避免序列化问题
Use the Merged JobDataMap
在任务执行的时候,JobExecutionContext中的JobDataMap作为一个convenience。它是通过在JobDetail中的JobDataMap和Trigger中的JobDataMap合并而来,后者中的值会覆盖前面一个中同名变量的值。
当你有一个任务在scheduler中,而且这个任务又会被多个Triggers重复使用,那么你最好把值存在Trigger的JobDataMap中,这样对于每次独立的任务触发时,你就可以为Job提供不同的数据输入啦。
根据以上所述,我们提出了如下的最佳实践:在调用Job.execute(…)方法时,一般来说应该从JobExecutionContext中的JobDataMap中解析变量的值,而不是直接从JobDetail的JobDataMap中解析。
Trigger 建议
Use TriggerUtils
- 提供了一个简单的方法来创建triggers(schedules)
- 有很多不同的方法通过schedules来创建triggers以满足特定的描述,这个要比直接实例化特定类型的triggers(SimpleTrigger,CronTrigger等)然后调用不同的setter方法来配置它们方便许多
- 提供了一个简单的方法来创建日期(比如start/end日期)
- 提供了分析triggers的助手(e.g. calculating future fire times)
JDBC JobStore
永远都不要直接往Quartz的表中写数据
Writing scheduling data directly to the database (via SQL) rather than using scheduling API:
永远不要在同一个Database将一个Non-Clustered Scheduler 指向另一个相同名字的Scheduler Name
If you point more than one scheduler instance at the same set of database tables, and one or more of those instances is not configured for clustering, any of the following may occur:
- 会造成数据腐化(被删除的数据,混乱的数据)
- 会造成任务在到达执行点的时候像没有执行就消失了
- 会造成当触发时间到来时,而任务还未执行
- 可能会造成死锁
Ensure Adequate Datasource Connection Size
建议将你的数据源连接数配置为配置为线程池中工作线程数加3。如果你的应用还要经常调用scheduler的API,那么你还需要增加额外的connections。If you are using JobStoreCMT, the “non managed” datasource should have a max connection size of at least four.
Daylight Savings Time
Avoid Scheduling Jobs Near the Transition Hours of Daylight Savings Time
注意:本地的时钟向前或者向后转移时和总的时间的细节可以在如下链接中找到:
https://en.wikipedia.org/wiki/Daylight_saving_time_by_country.
SimpleTriggers不受夏令时的影响,这是因为它们总是在毫秒时刻被精确地触发,并且在进过了精确的毫秒数之后会再次被触发。
由于CronTriggers会在给定的时/分/秒被触发,当夏令时转移时到来的时候,它们会受到这些怪事的影响。
举一个可能发生的例子,在夏令时的美国时区/位置进行调度的时候,如果使用CronTrigger并且调度的触发时间是在1:00 AM和2:00 AM之间时会发生下列的问题:
- 1:05 AM may occur twice! - duplicate firings on CronTrigger possible
- 2:05 AM may never occur! - missed firings on CronTrigger possible
Again, specifics of time and amount of adjustment varies by locale.
其他的触发器类型是根据日历的移动而不是根据确切的时间量来进行的,例如CalenderIntervalTrigger,将会同样地受影响,但不是错过触发或者触发两次,而是将它的触发时间偏移一个小时。
Jobs
等待条件来到
长时间运行的任务会阻止其他任务的运行(如果在线程池中所有的线程都繁忙)。
如果你认为需要调用 Thread.sleep() 这个方法来停止工作线程执行任务,这是一个典型的信号,任务不会完成其余的任务,因它必须等待某些条件的到来(比如某些数据可读)。
一个更好的方法是释放线程 (exit the job) 并且允许其他任务在这个线程执行。任务可以重新调度自己,或者在它退出之前其他任务。
抛出异常
一个任务的执行方法应该包含在try-catch块中,以此处理可能发生的异常。
如果一个任务抛出一个异常,Quartz一般会马上再执行它(可能会抛出相同的异常)。最好是任务捕获所有它可能遇到的异常并处理它们,然后重新调度自己或其他的任务。
可恢复性和幂等性
In-progress Jobs marked “recoverable” are automatically re-executed after a scheduler fails. This means some of the job’s “work” will be executed twice.
This means the job should be coded in such a way that its work is idempotent.
监听器(TriggerListener, JobListener, SchedulerListener)
Keep Code In Listeners Concise And Efficient
Keep Code In Listeners Concise And Efficient
Performing large amounts of work is discouraged, as the thread that would be executing the job (or completing the trigger and moving on to firing another job, etc.) will be tied up within the listener.
处理异常
每个监听器的方法都应该在try-catch块中处理所有可能的异常。
如果一个监听器抛出了一个异常,可能会造成其他的监听器无法被通知到或阻止其他任务的执行等。
通过应用来暴露调度器的功能
Be Careful of Security!
有的用户通过应用程序接口来暴露Quartz的调度功能。这会非常有用,虽它可能会造成极度的危险。
确保你没有错误地允许用户定义他们想要的任何参数和任何类型的任务。例如,Quartz会带有一个预定的任务org.quartz.jobs.NativeJob,这个任务将会在它们定义的任意的本地系统上执行命令。恶意的用户可能会使用这个来控制或者摧毁你的系统。
同样的像 SendEmailJob 之类的任务,并且事实上任何其他的任务都可以被当作恶意用途。
如果允许用户定义任意他们想要的任务将会是你的系统遭受各种可能的危害,等同于 OWASP 和 MITRE 定义的命令注入攻击等。
参考在此:
Quartz Best Practices(http://www.quartz-scheduler.org/documentation/best-practices.html)